package com.tbynet.jwp.service.impl;

import com.jfinal.aop.Inject;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Ret;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import com.jfinal.upload.UploadFile;
import com.tbynet.jwp.framework.aop.Aop;
import com.tbynet.jwp.framework.core.JwpService;
import com.tbynet.jwp.service.spi.AttachmentManager;
import com.tbynet.jwp.model.Options;
import com.tbynet.jwp.model.Postmeta;
import com.tbynet.jwp.model.Posts;
import com.tbynet.jwp.repository.spi.OptionService;
import com.tbynet.jwp.repository.spi.PostService;
import net.coobird.thumbnailator.Thumbnails;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 附件服务编排管理类
 * @ClassName: AttachmentManager
 * @Description: 负责附件上传、缩略图生成、元数据保存等复杂业务编排和事务管理
 * @Author: 佰亿互联
 * @Date: 2025年11月4日
 *
 * @Copyright: 2025 www.tbynet.com Inc. All rights reserved.
 * 注意: 本内容仅限于内部传阅，禁止外泄以及用于其他的商业目
 */
@Aop(AttachmentManager.class)
public class AttachmentServiceImpl extends JwpService implements AttachmentManager {

    @Inject
    private PostService postService;

    @Inject
    private OptionService optionService;

    /**
     * 上传附件完整业务流程
     * Manager层负责业务编排和事务管理
     *
     * @param uploadFile 上传的文件
     * @param postParent 父文章ID
     * @param postAuthor 作者ID
     * @return 上传结果
     */
    public Ret uploadAttachment(UploadFile uploadFile, String postParent, String postAuthor) {
        log.info("开始上传附件，文件名：{}", uploadFile.getFileName());

        try {
            // 1. 验证上传文件
            if (uploadFile == null || uploadFile.getFile() == null) {
                return Ret.fail("msg", "上传文件不能为空");
            }

            // 2. 生成存储路径
            String storagePath = generateStoragePath();
            log.debug("生成附件存储路径：{}", storagePath);

            // 3. 创建附件文章记录
            Posts attachmentPost = createAttachmentPost(uploadFile, storagePath, postParent, postAuthor);

            // 4. 生成附件元数据
            List<Postmeta> postmetas = generateAttachmentMetas(uploadFile, storagePath, attachmentPost);

            // 5. 在事务中保存附件数据
            boolean success = saveAttachmentInTransaction(attachmentPost, postmetas);

            if (success) {
                log.info("上传附件成功，附件ID：{}，文件名：{}", attachmentPost.getID(), uploadFile.getFileName());
                return Ret.ok("msg", "上传文件成功").set("data", attachmentPost);
            } else {
                log.error("上传附件失败，文件名：{}", uploadFile.getFileName());
                // 删除已上传的文件（事务失败时清理）
                cleanupUploadedFile(uploadFile);
                return Ret.fail("msg", "上传文件失败，请联系管理员");
            }
        } catch (Exception e) {
            log.error("上传附件流程异常，文件名：{}", uploadFile.getFileName(), e);
            // 异常时清理已上传的文件
            cleanupUploadedFile(uploadFile);
            return Ret.fail("msg", "上传文件异常: " + e.getMessage());
        }
    }

    /**
     * 生成附件存储路径
     * Manager层负责业务逻辑编排
     */
    private String generateStoragePath() {
        String uploadsUseYearmonthFolders = optionService.getOptionValue(Options.uploads_use_yearmonth_folders);
        if ("1".equals(uploadsUseYearmonthFolders)) {
            return String.format("%s%s%s",
                    new java.text.SimpleDateFormat("yyyy").format(new Date()),
                    File.separator,
                    new java.text.SimpleDateFormat("MM").format(new Date()));
        }
        return "";
    }

    /**
     * 创建附件文章记录
     * Manager层负责业务对象构建
     */
    private Posts createAttachmentPost(UploadFile uploadFile, String storagePath, String postParent, String postAuthor) {
        String fileName = uploadFile.getFileName();
        String name = fileName.substring(0, fileName.lastIndexOf("."));

        Posts post = new Posts();
        post.setPostType(Posts.POST_TYPE_ATTACHMENT);
        post.setPostStatus(Posts.POST_STATUS_INHERIT);
        post.setPostTitle(name);
        post.setPostName(java.net.URLEncoder.encode(name));
        post.setPostMimeType(uploadFile.getContentType());
        post.setPostParent(new BigInteger(postParent != null ? postParent : "0"));

        // 设置GUID（实际URL路径）
        String siteUrl = optionService.getOptionValue(Options.siteurl);
        String guid = String.format("%s/uploads/%s/%s", siteUrl, storagePath, fileName);
        post.setGuid(guid);

        post.setPostContent("");
        post.setPostContentFiltered("");
        post.setPostExcerpt("");
        post.setToPing("");
        post.setPinged("");
        post.setCommentStatus("closed");
        post.setPingStatus("closed");
        post.setMenuOrder(0);
        post.setCommentCount(0L);
        post.setPostAuthor(new BigInteger(postAuthor != null ? postAuthor : "1"));

        Date now = new Date();
        post.setPostDate(now);
        post.setPostDateGmt(now);
        post.setPostModified(now);
        post.setPostModifiedGmt(now);

        return post;
    }

    /**
     * 生成附件元数据
     * Manager层负责复杂的元数据生成逻辑
     */
    private List<Postmeta> generateAttachmentMetas(UploadFile uploadFile, String storagePath, Posts attachmentPost) {
        List<Postmeta> postmetas = new ArrayList<>();
        File file = uploadFile.getFile();
        String fileName = uploadFile.getFileName();

        try {
            // 1. 添加附件文件路径元数据
            Postmeta attachedFileMeta = new Postmeta();
            attachedFileMeta.setMetaKey(Postmeta._wp_attached_file);
            attachedFileMeta.setMetaValue(storagePath + File.separator + fileName);
            attachedFileMeta.setPostId(attachmentPost.getID() != null ?
                    new BigInteger(attachmentPost.getID().toString()) : null);
            postmetas.add(attachedFileMeta);

            // 2. 如果是图片，生成图片元数据和缩略图
            BufferedImage image = ImageIO.read(file);
            if (image != null) {
                Postmeta attachmentMetadata = generateImageMetadata(uploadFile, image, storagePath);
                if (attachmentMetadata != null) {
                    attachmentMetadata.setPostId(attachmentPost.getID() != null ?
                            new BigInteger(attachmentPost.getID().toString()) : null);
                    postmetas.add(attachmentMetadata);
                }
            } else {
                // 3. 非图片文件，添加编辑锁元数据
                Postmeta editLock = new Postmeta();
                editLock.setMetaKey(Postmeta._edit_lock);
                editLock.setMetaValue(System.currentTimeMillis() + ":1");
                editLock.setPostId(attachmentPost.getID() != null ?
                        new BigInteger(attachmentPost.getID().toString()) : null);
                postmetas.add(editLock);
            }
        } catch (Exception e) {
            log.error("生成附件元数据失败，文件名：{}", fileName, e);
        }

        return postmetas;
    }

    /**
     * 生成图片元数据（包含缩略图生成）
     * Manager层负责复杂的图片处理逻辑
     */
    private Postmeta generateImageMetadata(UploadFile uploadFile, BufferedImage image, String storagePath) {
        try {
            File file = uploadFile.getFile();
            String fileName = uploadFile.getFileName();
            String name = fileName.substring(0, fileName.lastIndexOf("."));
            String suffix = fileName.substring(fileName.lastIndexOf("."));

            int width = image.getWidth();
            int height = image.getHeight();

            // 构建基础图片信息
            Kv imageInfo = Kv.by("width", width)
                    .set("height", height)
                    .set("file", fileName);

            // 生成各种尺寸的缩略图
            Kv sizes = generateThumbnails(uploadFile, name, suffix, width, height);
            imageInfo.set("sizes", sizes);

            // 添加图片元信息
            Kv imageMeta = Kv.by("aperture", "0")
                    .set("credit", "")
                    .set("camera", "")
                    .set("caption", "")
                    .set("created_timestamp", "0")
                    .set("copyright", "")
                    .set("focal_length", "0")
                    .set("iso", "0")
                    .set("shutter_speed", "0")
                    .set("title", "")
                    .set("orientation", "0")
                    .set("keywords", Kv.create());

            imageInfo.set("image_meta", imageMeta);

            // 创建元数据对象
            Postmeta metadata = new Postmeta();
            metadata.setMetaKey(Postmeta._wp_attachment_metadata);
            metadata.setMetaValue(imageInfo.toJson()); // 假设使用JSON存储

            return metadata;
        } catch (Exception e) {
            log.error("生成图片元数据失败，文件名：{}", uploadFile.getFileName(), e);
            return null;
        }
    }

    /**
     * 生成各种尺寸的缩略图
     * Manager层负责缩略图生成逻辑
     */
    private Kv generateThumbnails(UploadFile uploadFile, String name, String suffix,
                                  int originalWidth, int originalHeight) {
        Kv sizes = Kv.create();
        String uploadPath = uploadFile.getUploadPath();
        File originalFile = uploadFile.getFile();

        try {
            // 缩略图尺寸
            generateThumbnailSize(sizes, uploadPath, originalFile, name, suffix,
                    Options.thumbnail_size_w, Options.thumbnail_size_h,
                    Options.thumbnail_crop, "thumbnail", originalWidth, originalHeight);

            // 中等尺寸
            generateThumbnailSize(sizes, uploadPath, originalFile, name, suffix,
                    Options.medium_size_w, Options.medium_size_h,
                    null, "medium", originalWidth, originalHeight);

            // 大尺寸
            generateThumbnailSize(sizes, uploadPath, originalFile, name, suffix,
                    Options.large_size_w, Options.large_size_h,
                    null, "large", originalWidth, originalHeight);

        } catch (Exception e) {
            log.error("生成缩略图失败，文件名：{}", uploadFile.getFileName(), e);
        }

        return sizes;
    }

    /**
     * 生成指定尺寸的缩略图
     */
    private void generateThumbnailSize(Kv sizes, String uploadPath, File originalFile,
                                       String name, String suffix, String sizeWOption,
                                       String sizeHOption, String cropOption,
                                       String sizeName, int originalWidth, int originalHeight) {
        try {
            String sizeWStr = optionService.getOptionValue(sizeWOption);
            String sizeHStr = optionService.getOptionValue(sizeHOption);

            if (sizeWStr != null && sizeHStr != null) {
                int sizeW = Integer.parseInt(sizeWStr);
                int sizeH = Integer.parseInt(sizeHStr);

                // 检查是否需要生成缩略图
                boolean needsThumbnail = originalWidth > sizeW || originalHeight > sizeH;
                if (cropOption != null) {
                    String crop = optionService.getOptionValue(cropOption);
                    needsThumbnail = needsThumbnail || "1".equals(crop);
                }

                if (needsThumbnail) {
                    String thumbnailFileName = name + "-" + sizeW + "x" + sizeH + suffix;
                    File thumbnailFile = new File(uploadPath + File.separator + thumbnailFileName);

                    Thumbnails.of(originalFile)
                            .size(sizeW, sizeH)
                            .toFile(thumbnailFile);

                    Kv sizeInfo = Kv.by("file", thumbnailFileName)
                            .set("width", sizeW)
                            .set("height", sizeH)
                            .set("mime-type", "image/jpeg"); // 简化处理，实际应根据文件类型判断

                    sizes.set(sizeName, sizeInfo);

                    log.debug("生成缩略图成功，尺寸：{}，文件名：{}", sizeName, thumbnailFileName);
                }
            }
        } catch (Exception e) {
            log.error("生成{}尺寸缩略图失败", sizeName, e);
        }
    }

    /**
     * 在事务中保存附件数据
     * Manager层负责事务管理
     */
    private boolean saveAttachmentInTransaction(Posts attachmentPost, List<Postmeta> postmetas) {
        return Db.tx(new IAtom() {
            @Override
            public boolean run() throws SQLException {
                try {
                    // 1. 保存附件文章记录
                    if (!postService.save(attachmentPost)) {
                        log.error("保存附件文章记录失败");
                        return false;
                    }

                    // 2. 保存附件元数据
                    if (!postService.savePostMetas(attachmentPost.getID(), postmetas)) {
                        log.error("保存附件元数据失败");
                        return false;
                    }

                    return true;
                } catch (Exception e) {
                    log.error("保存附件事务执行失败", e);
                    throw new SQLException("保存附件事务失败", e);
                }
            }
        });
    }

    /**
     * 清理已上传的文件（事务失败时）
     */
    private void cleanupUploadedFile(UploadFile uploadFile) {
        try {
            if (uploadFile != null && uploadFile.getFile() != null) {
                File file = uploadFile.getFile();
                if (file.exists()) {
                    file.delete();
                    log.debug("清理上传文件：{}", file.getAbsolutePath());
                }
            }
        } catch (Exception e) {
            log.error("清理上传文件失败", e);
        }
    }
}